#include <openssl/ssl.h>
#include <stdexcept>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>

class HttpsClient
{
public:
    HttpsClient()
    {
        SSL_library_init();
        SSL_load_error_strings();
        OpenSSL_add_all_algorithms();

        this->ctx_ = ::SSL_CTX_new(::TLS_client_method());
        if (!this->ctx_)
        {
            throw std::runtime_error("Failed to create SSL context");
        }
    }

    ~HttpsClient()
    {
        if (this->ssl_)
        {
            ::SSL_free(this->ssl_);
        }
        if (this->ctx_)
        {
            ::SSL_CTX_free(this->ctx_);
        }
        if (this->sock_ > 0)
        {
            ::close(this->sock_);
        }
    }

    void connect(const std::string &host, int port)
    {
        this->sock_ = ::socket(AF_INET, SOCK_STREAM, 0);
        if (this->sock_ < 0)
        {
            throw std::runtime_error("Failed to create socket");
        }

        struct sockaddr_in addr{};
        addr.sin_family = AF_INET;
        addr.sin_port = ::htons(port);
        addr.sin_addr.s_addr = ::inet_addr(host.c_str());
        if (::connect(this->sock_, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        {
            throw std::runtime_error("Failed to connect");
        }

        this->ssl_ = ::SSL_new(this->ctx_);
        if (!this->ssl_)
        {
            throw std::runtime_error("Failed to create SSL");
        }

        ::SSL_set_fd(this->ssl_, this->sock_);
        if (::SSL_connect(this->ssl_) <= 0)
        {
            throw std::runtime_error("Failed to establish SSL connection");
        }
        std::cout << "Connected using: " << SSL_get_cipher(this->ssl_) << "\n";
    }

    void sendGetRequest(const std::string &path)
    {
        std::cout << "发送请求" << std::endl;

        std::string request =
            "GET " + path + " HTTP/1.1\r\n"
                            "Host: localhost\r\n"
                            "Connection: close\r\n"
                            "\r\n";
        if (::SSL_write(this->ssl_, request.c_str(), request.length()) <= 0)
        {
            throw std::runtime_error("Failed to send request");
        }

        char buffer[4096]{};
        int bytes{};
        while ((bytes = ::SSL_read(this->ssl_, buffer, sizeof(buffer) - 1)) > 0)
        {
            buffer[bytes] = 0;
            std::cout << buffer << std::endl;
        }
    }

private:
    SSL_CTX *ctx_;
    int sock_;
    SSL *ssl_;
};

int main()
{
    try
    {
        HttpsClient client{};

        client.connect("127.0.0.1", 8633);
        std::cout << "连接成功" << std::endl;

        client.sendGetRequest("/");

        return 0;
    }
    catch (const std::exception &e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
}